from abc import ABCMeta, abstractmethod
from typing import (Any, Callable, List, Literal, NoReturn, Optional, Tuple,
                    Type, Union)

from sty import bg, fg, rs

try:
    from .entity import Entity
    from .event import Event
    from .events import EventTypes
    from .player import Player
except ImportError:
    from entity import Entity  # type: ignore
    from event import Event  # type: ignore
    from events import EventTypes  # type: ignore
    from player import Player  # type: ignore


Number = Union[int, float]
__all__ = [
    # Natural
    'River', 'Mountain', 'Tree',
    # Mineral
    'Mineral', 'Wood', 'Stone', 'Metal',
    # Unit
    'Unit', 'Warrior',
    # Building
    'Building', 'Base', 'Lab',
]


# ABC for minerals.
class Mineral(Entity):
    def __init__(self, max_times: int = 10):
        self.times = max_times

    def char_rep(self) -> NoReturn:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """

    def place_river(self) -> Literal[True]:
        """Returns whether a river can be placed at this spot.

        Returns:
            - `bool`: Whether a river can be placed here.
        """
        return True

    def place_mountain(self) -> Literal[False]:
        """Returns whether a mountain can be placed at this spot.

        Returns:
            - `bool`: Whether a mountain can be placed here.
        """
        return False

    def place_tree(self) -> Literal[False]:
        """Returns whether a tree can be placed at this spot.

        Returns:
            - `bool`: Whether a tree can be placed here.
        """
        return False

    def place_mineral(self) -> Literal[True]:
        """Returns whether minerals can be placed at this spot.

        Returns:
            - `bool`: Whether minerals can be placed here.
        """
        return True

    def place_building(self, building: 'Building') -> Literal[False]:
        """Returns whether a building can be built at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be built at this spot.
        """
        return False

    def crossable(self, unit: 'Unit') -> Literal[True]:
        """Returns whether a unit can cross this entity.

        Args:
            - `unit` (`Unit`): The unit that is trying to cross the entity.

        Returns:
            - `bool`: Whether a unit can cross this entity.
        """
        return True

    __repr__ = char_rep


# ABC for all units.
class Unit(Entity, metaclass=ABCMeta):
    """A basic unit/person.
    """

    def __init__(self, owner: Optional[Player] = None):
        self.inventory: List[str] = []  # TODO type
        self.owner: Optional[Player] = owner
        self.damage: Number = 0

    def backgrnd_rep(self) -> NoReturn:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """

    def place_river(self) -> Literal[False]:
        """Returns whether a river can be placed at this spot.

        Returns:
            - `bool`: Whether a river can be placed here.
        """
        return False

    def place_mountain(self) -> Literal[False]:
        """Returns whether a mountain can be placed at this spot.

        Returns:
            - `bool`: Whether a mountain can be placed here.
        """
        return False

    def place_tree(self) -> Literal[False]:
        """Returns whether a tree can be placed at this spot.

        Returns:
            - `bool`: Whether a tree can be placed here.
        """
        return False

    def place_mineral(self) -> Literal[False]:
        """Returns whether minerals can be placed at this spot.

        Returns:
            - `bool`: Whether minerals can be placed here.
        """
        return False

    def place_building(self, building: 'Building') -> Literal[False]:
        """Returns whether a building can be built at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be built at this spot.
        """
        return False

    def crossable(self, unit: 'Unit') -> bool:
        """Returns whether a unit can cross this entity.

        Args:
            - `unit` (`Unit`): The unit that is trying to cross the entity.

        Returns:
            - `bool`: Whether a unit can cross this entity.
        """
        return unit.owner == self.owner

    @abstractmethod
    def cross_river(self) -> bool:
        """Returns whether this unit can cross a river.

        Returns:
            - `bool`: Whether this unit can cross a river.
        """

    @abstractmethod
    def total_hp(self) -> Number:
        """Returns the HPs of the unit.

        Returns:
            - `Union[int, float]`: The HPs of the unit.
        """

    @abstractmethod
    def hp(self) -> Number:
        """Returns the HPs of the unit at the moment.

        Returns:
            - `Union[int, float]`: The HPs of the unit at the moment.
        """

    @abstractmethod
    def a2u(self, unit: 'Unit') -> Number:
        """Returns the HPs this unit can deduct when attacking another
        unit.

        Args:
            - `unit` (`Unit`): The other unit.

        Returns:
            - `Union[int, float]`: The HPs this unit can deduct when
            attacking the other unit.
        """

    @abstractmethod
    def a2b(self, building: 'Building') -> Number:
        """Returns the points this unit can deduct when attacking a building.

        Args:
            - `building` (`Building`): The building being attacked.

        Returns:
            - `Union[int, float]`: The points this unit can deduct when
            attacking the other building.
        """

    @abstractmethod
    def mend(self) -> Number:
        """Returns the HPs this unit can mend per turn when it is not
        engaged in combat.

        Returns:
            - `Union[int, float]`: The HPs this unit can mend per turn when
            it is not engaged in combat.
        """

    @abstractmethod
    def movement(self) -> Number:
        """Return how many blocks this unit can move each turn.

        Returns:
            - `Union[int, float]`: The number of blocks this unit can move
            each turn.
        """

    @abstractmethod
    def range(self) -> int:
        """Returns the attack range of the unit.

        Returns:
            - `int`: The attack range of the unit.
        """


# ABC for all buildings.
class Building(Entity, metaclass=ABCMeta):
    """A basic building.
    """

    def __init__(self, owner: Player):
        self.owner: Player = owner
        self.damage: Number = 0
        self.inventory: List[Any] = []

    def place_river(self) -> Literal[False]:
        """Returns whether a river can be placed at this spot.

        Returns:
            - `bool`: Whether a river can be placed here.
        """
        return False

    def place_mountain(self) -> Literal[False]:
        """Returns whether a mountain can be placed at this spot.

        Returns:
            - `bool`: Whether a mountain can be placed here.
        """
        return False

    def place_tree(self) -> Literal[False]:
        """Returns whether a tree can be placed at this spot.

        Returns:
            - `bool`: Whether a tree can be placed here.
        """
        return False

    def place_mineral(self) -> Literal[False]:
        """Returns whether minerals can be placed at this spot.

        Returns:
            - `bool`: Whether minerals can be placed here.
        """
        return False

    def place_building(self, building: 'Building') -> Literal[False]:
        """Returns whether a building can be built at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be built at this spot.
        """
        return False

    def crossable(self, unit: Unit) -> Literal[False]:
        """Returns whether a unit can cross this entity.

        Args:
            - `unit` (`Unit`): The unit that is trying to cross the entity.

        Returns:
            - `bool`: Whether a unit can cross this entity.
        """
        return False

    @abstractmethod
    def total_hp(self) -> Number:
        """Returns the HPs of the unit.

        Returns:
            - `Union[int, float]`: The HPs of the unit.
        """

    @abstractmethod
    def hp(self) -> Number:
        """Returns the HPs of the unit at the moment.

        Returns:
            - `Union[int, float]`: The HPs of the unit at the moment.
        """

    @abstractmethod
    def a2u(self, unit: 'Unit') -> Number:
        """Returns the HPs this unit can deduct when attacking another
        unit.

        Args:
            - `unit` (`Unit`): The other unit.

        Returns:
            - `Union[int, float]`: The HPs this unit can deduct when
            attacking the other unit.
        """

    @abstractmethod
    def a2b(self, building: 'Building') -> Number:
        """Returns the points this unit can deduct when attacking a building.

        Args:
            - `building` (`Building`): The building being attacked.

        Returns:
            - `Union[int, float]`: The points this unit can deduct when
            attacking the other building.
        """

    @abstractmethod
    def mend(self) -> Number:
        """Returns the HPs this unit can mend per turn when it is not
        engaged in combat.

        Returns:
            - `Union[int, float]`: The HPs this unit can mend per turn when
            it is not engaged in combat.
        """

    @abstractmethod
    def range(self) -> int:
        """Returns the attack range of the unit.

        Returns:
            - `int`: The attack range of the unit.
        """


# Real class definitions.
# Natural stuff.
class River(Entity):
    """A river.
    """

    def char_rep(self) -> NoReturn:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """

    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        return (bg.li_blue + fg.black, rs.fg + rs.bg)

    def place_river(self) -> Literal[True]:
        """Returns whether a river can be placed at this spot.

        Returns:
            - `bool`: Whether a river can be placed here.
        """
        return True

    def place_mountain(self) -> Literal[False]:
        """Returns whether a mountain can be placed at this spot.

        Returns:
            - `bool`: Whether a mountain can be placed here.
        """
        return False

    def place_tree(self) -> Literal[False]:
        """Returns whether a tree can be placed at this spot.

        Returns:
            - `bool`: Whether a tree can be placed here.
        """
        return False

    def place_mineral(self) -> Literal[False]:
        """Returns whether minerals can be placed at this spot.

        Returns:
            - `bool`: Whether minerals can be placed here.
        """
        return False

    def place_building(self, building: Building) -> bool:
        """Returns whether a building can be built at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be built at this spot.
        """
        return False

    def crossable(self, unit: Unit) -> bool:
        """Returns whether a unit can cross this entity.

        Args:
            - `unit` (`Unit`): The unit that is trying to cross the entity.

        Returns:
            - `bool`: Whether a unit can cross this entity.
        """
        return unit.cross_river()

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.print_pos:
                if ev.pos == position:
                    ev.print.append('A river is crossing here.')
        return callback

    def __repr__(self) -> str:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """
        return self.backgrnd_rep() + ' ' + rs.bg


class Mountain(Entity):
    """A mountain.
    """

    def char_rep(self) -> NoReturn:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """

    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        return (bg.li_grey + fg.black, rs.fg + rs.bg)

    def place_river(self) -> Literal[False]:
        """Returns whether a river can be placed at this spot.

        Returns:
            - `bool`: Whether a river can be placed here.
        """
        return False

    def place_mountain(self) -> Literal[False]:
        """Returns whether a mountain can be placed at this spot.

        Returns:
            - `bool`: Whether a mountain can be placed here.
        """
        return False

    def place_tree(self) -> Literal[False]:
        """Returns whether a tree can be placed at this spot.

        Returns:
            - `bool`: Whether a tree can be placed here.
        """
        return False

    def place_mineral(self) -> Literal[True]:
        """Returns whether minerals can be placed at this spot.

        Returns:
            - `bool`: Whether minerals can be placed here.
        """
        return True

    def place_building(self, building: Building) -> bool:
        """Returns whether a building can be built at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be built at this spot.
        """
        return False

    def crossable(self, unit: Unit) -> Literal[False]:
        """Returns whether a unit can cross this entity.

        Args:
            - `unit` (`Unit`): The unit that is trying to cross the entity.

        Returns:
            - `bool`: Whether a unit can cross this entity.
        """
        return False

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.print_pos:
                if ev.pos == position:
                    ev.print.append('A mountain is here.')
        return callback

    def __repr__(self) -> str:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """
        return self.backgrnd_rep() + ' ' + rs.bg


class Tree(Entity):
    """A tree.
    """

    def char_rep(self) -> NoReturn:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """

    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        return (bg.li_green + fg.black, rs.fg + rs.bg)

    def place_river(self) -> Literal[False]:
        """Returns whether a river can be placed at this spot.

        Returns:
            - `bool`: Whether a river can be placed here.
        """
        return False

    def place_mountain(self) -> Literal[False]:
        """Returns whether a mountain can be placed at this spot.

        Returns:
            - `bool`: Whether a mountain can be placed here.
        """
        return False

    def place_tree(self) -> Literal[False]:
        """Returns whether a tree can be placed at this spot.

        Returns:
            - `bool`: Whether a tree can be placed here.
        """
        return False

    def place_mineral(self) -> Literal[False]:
        """Returns whether minerals can be placed at this spot.

        Returns:
            - `bool`: Whether minerals can be placed here.
        """
        return False

    def place_building(self, building: Building) -> bool:
        """Returns whether a building can be built at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be built at this spot.
        """
        return False

    def crossable(self, unit: Unit) -> Literal[True]:
        """Returns whether a unit can cross this entity.

        Args:
            - `unit` (`Unit`): The unit that is trying to cross the entity.

        Returns:
            - `bool`: Whether a unit can cross this entity.
        """
        return True

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.print_pos:
                if ev.pos == position:
                    ev.print.append('A tree is here.')
        return callback

    def __repr__(self) -> str:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """
        return self.backgrnd_rep() + ' ' + rs.bg


# Minerals.
class Wood(Mineral):
    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        return (bg(101, 67, 33), bg.rs)

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.mine_wood:
                if ev.pos == position:
                    board[position[0]][position[1]].remove(self)
            elif ev.type == EventTypes.print_pos:
                ev.print.append(
                    'There is some wood here to be mined, %i times left.' % self.times)
        return callback


class Stone(Mineral):
    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        return (bg.grey + fg.black, fg.rs + bg.rs)

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.mine_stone:
                if ev.pos == position:
                    board[position[0]][position[1]].remove(self)
            elif ev.type == EventTypes.print_pos:
                ev.print.append(
                    'There is some stone here to be mined, %i times left.' % self.times)
        return callback


class Metal(Mineral):
    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        return (bg(212, 175, 55) + fg.black, fg.rs + bg.rs)

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.mine_metal:
                if ev.pos == position:
                    board[position[0]][position[1]].remove(self)
            elif ev.type == EventTypes.print_pos:
                ev.print.append(
                    'There is some metal here to be mined, %i times left.' % self.times)
        return callback


# Units.
class Warrior(Unit):
    def char_rep(self) -> Literal['w']:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """
        return 'w'

    def cross_river(self) -> bool:
        """Returns whether this unit can cross a river.

        Returns:
            - `bool`: Whether this unit can cross a river.
        """
        return 'boat' in self.inventory

    def total_hp(self) -> Literal[5]:
        """Returns the HPs of the unit.

        Returns:
            - `Union[int, float]`: The HPs of the unit.
        """
        return 5

    def hp(self) -> Number:
        """Returns the HPs of the unit at the moment.

        Returns:
            - `Union[int, float]`: The HPs of the unit at the moment.
        """
        return self.total_hp() - self.damage

    def a2u(self, unit: 'Unit') -> Literal[1]:
        """Returns the HPs this unit can deduct when attacking another
        unit.

        Args:
            - `unit` (`Unit`): The other unit.

        Returns:
            - `Union[int, float]`: The HPs this unit can deduct when
            attacking the other unit.
        """
        return 1

    def a2b(self, building: 'Building') -> Literal[1]:
        """Returns the points this unit can deduct when attacking a building.

        Args:
            - `building` (`Building`): The building being attacked.

        Returns:
            - `Union[int, float]`: The points this unit can deduct when
            attacking the other building.
        """
        return 1

    def mend(self) -> float:
        """Returns the HPs this unit can mend per turn when it is not
        engaged in combat.

        Returns:
            - `Union[int, float]`: The HPs this unit can mend per turn when
            it is not engaged in combat.
        """
        return 0.5

    def movement(self) -> Literal[1]:
        """Return how many blocks this unit can move each turn.

        Returns:
            - `Union[int, float]`: The number of blocks this unit can move
            each turn.
        """
        return 1

    def range(self) -> Literal[1]:
        """Returns the attack range of the unit.

        Returns:
            - `int`: The attack range of the unit.
        """
        return 1

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.move_entity:
                if ev.entity is self:
                    board[position[0]][position[1]].remove(self)
                    board[ev.stoppos[0]][ev.stoppos[1]].append(self)
            elif ev.type == EventTypes.print_pos:
                if ev.pos == position:
                    ev.print.append(
                        'There is a warrior of player %s here.' % self.owner.name)
            elif ev.type == EventTypes.attack:
                if ev.attacked is self:
                    self.damage += ev.attacker.a2u(self)
                    # Other side will print
                elif ev.attacker is self:
                    if issubclass(ev.attacked.__class__, Unit):
                        ev.print.append('Attacked unit %s, caused %f damage!' % (
                            ev.attacked.__class__.__name__, self.a2u(ev.attacked)))
                    else:
                        ev.print.append('Attacked building %s, caused %f damage!' % (
                            ev.attacked.__class__.__name__, self.a2b(ev.attacked)))
            elif ev.type == EventTypes.tick:
                if self.damage:
                    self.damage = max(0, self.damage - self.mend())
        return callback

    __repr__ = char_rep


# Buildings.
class Base(Building):
    """Special "building": a player's base.
    """

    def __init__(self, owner: Player, color: Optional[str] = None):
        super().__init__(owner)
        if color is None and owner.color:
            color = owner.color
        self._color = color

    def char_rep(self) -> NoReturn:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """

    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        return (self._color, rs.bg)

    def place_building(self, building: Building) -> bool:
        """Returns whether a building can be built at this spot.

        Args:
            - `building` (`Building`): The building to place.

        Returns:
            - `bool`: Whether the building can be built at this spot.
        """
        return building.owner == self.owner

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.print_pos:
                if ev.pos == position:
                    ev.print.append(
                        'Player %s\'s base extends here.' % self.owner.name)
        return callback

    def total_hp(self) -> float:
        """Returns the HPs of the unit.

        Returns:
            - `Union[int, float]`: The HPs of the unit.
        """
        return float('inf')

    def hp(self) -> float:
        """Returns the HPs of the unit at the moment.

        Returns:
            - `Union[int, float]`: The HPs of the unit at the moment.
        """
        return self.total_hp()

    def a2u(self, unit: Unit) -> Literal[0]:
        """Returns the HPs this unit can deduct when attacking another
        unit.

        Args:
            - `unit` (`Unit`): The other unit.

        Returns:
            - `Union[int, float]`: The HPs this unit can deduct when
            attacking the other unit.
        """
        return 0

    def a2b(self, building: Building) -> Literal[0]:
        """Returns the points this unit can deduct when attacking a building.

        Args:
            - `building` (`Building`): The building being attacked.

        Returns:
            - `Union[int, float]`: The points this unit can deduct when
            attacking the other building.
        """
        return 0

    def mend(self) -> float:
        """Returns the HPs this unit can mend per turn when it is not
        engaged in combat.

        Returns:
            - `Union[int, float]`: The HPs this unit can mend per turn when
            it is not engaged in combat.
        """
        return float('inf')

    def range(self) -> Literal[0]:
        """Returns the attack range of the unit.

        Returns:
            - `int`: The attack range of the unit.
        """
        return 0

    def __repr__(self) -> str:
        return self.backgrnd_rep()[0] + ' ' + self.backgrnd_rep()[1]


class Lab(Building):

    RESEARCHES = {
        'fishing': ['fishing', 4, 4],
        'healing': ['healing', 6, 6],
        'boat': ['boat', 3, 3],
        'bow&arrow': ['bow&arrow', 5, 5],
        'riding': ['riding', 5, 5],
        'tool': ['tool', 2, 2]
    }

    def __init__(self, owner: Player, builtwith: Union[Type[Wood], Type[Stone], Type[Metal], None] = None):
        super().__init__(owner)
        self.builtwith = builtwith
        self.in_progress = None

    def char_rep(self) -> str:
        """Returns the character that can represent the entity.

        Returns:
            - `Optional[str]`: The representative character, or `None` if
            there is no representative character.
        """
        return 'L'

    def backgrnd_rep(self) -> Tuple[str, str]:
        """Returns the ANSI code that sets the background color that
        should be used when displaying this entity and the reset code.

        Returns:
            - `Optional[Tuple[str, str]]`: The background ANSI code and its
            reset code, or `None` if there is no need for background color.
        """
        if self.builtwith is Wood:
            return Wood().backgrnd_rep()
        elif self.builtwith is Stone:
            return Stone().backgrnd_rep()
        elif self.builtwith is Metal:
            return Metal().backgrnd_rep()
        else:
            return ('', '')

    def onevent(self, ev: Event) -> Callable[[Any, Tuple[int, int], Event], NoReturn]:
        """Event callback.

        Returns:
            - `Optional[Callable[[Board, Tuple[int, int], Event], int]]`:
            The callback function to be called when an event happens. Takes
            the current board, the position of the entity in (column, row)
            form, and the event as arguments. Does not return anything.
        """
        def callback(board: Any, position: Tuple[int, int], ev: Event) -> NoReturn:
            """Callback function to be called when an event happens.

            Args:
                - `board` (`Board`): The current board.
                - `position` (`Tuple[int, int]`): The position of the unit
                in (column, row) form.
            """
            if ev.type == EventTypes.print_pos:
                if ev.pos == position:
                    ev.print.append(
                        'There is a lab of player %s here.' % self.owner.name)
            elif ev.type == EventTypes.get_stat:
                if self in ev.entities:
                    ev.status = {
                        'in_progress': self.in_progress
                    }
            elif ev.type == EventTypes.attack:
                if ev.attacked is self:
                    self.damage += ev.attacker.a2b(self)
                    # Other side will print
            elif ev.type == EventTypes.tick:
                if self.damage:
                    self.damage = max(0, self.damage - self.mend())
                if self.in_progress is not None:
                    self.in_progress[2] -= 1
                    if self.in_progress[2] == 0:
                        ev.print.append('Lab finished technology %s.' %
                                        self.in_progress[0].__name__)
                    self.in_progress = None
            elif ev.type == EventTypes.research:
                if self.in_progress is not None:
                    ev.success = False
                else:
                    if ev.name not in Lab.RESEARCHES:
                        ev.success = False
                    else:
                        self.in_progress = Lab.RESEARCHES[ev.name]
                        ev.success = True
        return callback

    def total_hp(self) -> Literal[10]:
        """Returns the HPs of the unit.

        Returns:
            - `Union[int, float]`: The HPs of the unit.
        """
        return 10

    def hp(self) -> Number:
        """Returns the HPs of the unit at the moment.

        Returns:
            - `Union[int, float]`: The HPs of the unit at the moment.
        """
        return self.total_hp() - self.damage

    def a2u(self, unit: 'Unit') -> Literal[0]:
        """Returns the HPs this unit can deduct when attacking another
        unit.

        Args:
            - `unit` (`Unit`): The other unit.

        Returns:
            - `Union[int, float]`: The HPs this unit can deduct when
            attacking the other unit.
        """
        return 0

    def a2b(self, building: 'Building') -> Literal[0]:
        """Returns the points this unit can deduct when attacking a building.

        Args:
            - `building` (`Building`): The building being attacked.

        Returns:
            - `Union[int, float]`: The points this unit can deduct when
            attacking the other building.
        """
        return 0

    def mend(self) -> float:
        """Returns the HPs this unit can mend per turn when it is not
        engaged in combat.

        Returns:
            - `Union[int, float]`: The HPs this unit can mend per turn when
            it is not engaged in combat.
        """
        return 0.5

    def range(self) -> Literal[0]:
        """Returns the attack range of the unit.

        Returns:
            - `int`: The attack range of the unit.
        """
        return 0

    def __repr__(self) -> str:
        return self.backgrnd_rep()[0] + self.char_rep() + self.backgrnd_rep()[1]


if __name__ == '__main__':
    River()
    Mountain()
    Tree()
    Wood()
    Stone()
    Metal()
    Warrior()
    Base(Player())
    Lab(Player(), Wood)
    print(Lab(Player(), Wood))
    print(Lab(Player(), Stone))
    print(Lab(Player(), Metal))
